#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>

#include "map_reduce.h"
#include "stddefines.h"

const int DIST = 100;
int N;
int INFINITY;

int my_key_cmp(const void *s1, const void *s2)
{    
    short val1 = *((short *)s1);
    short val2 = *((short *)s2);
    
    if (val1 < val2) {
        return -1;
    }
    else if (val1 > val2) {
        return 1;
    }
    else {
        return 0;
    }
}

void *my_locator (map_args_t *task)
{
    assert (task);
    return task->data;
}

void my_map(map_args_t *args)
{
    int i,j;
	assert(args);
    int *adj = (int *)args->data;
    assert(adj);
    intptr_t ptr[INFINITY];
    for(i = 0;i < INFINITY;i++)
		ptr[i] = i;
    for (i = 0; i < args->length; i++)
    {
		int dist1 = adj[i*(N+1)];
		if(dist1 != INFINITY)		
			for(j = 0;j < N;j++)
			{
				int dist2 = adj[i*(N+1)+1+j];
				if(dist2 != INFINITY)
				{
					int new_dist = dist1+dist2;
					emit_intermediate((void*)(short*)&(ptr[j]),
					(void*)(short*)ptr[new_dist],sizeof(short));
				}
			}
    }
}

void my_reduce(void *key_in, iterator_t *itr)
{
    short *key = (short *)key_in;
    void *val;
    intptr_t dist = INFINITY;

    assert(key);
    assert(itr);

    while (iter_next (itr, &val))
    {
		if((intptr_t)val < dist)
			dist = (intptr_t)val;
    }
    emit(key,(void *)dist);
}

void *my_combiner (iterator_t *itr)
{
    void *val;
    intptr_t dist = INFINITY;
    assert(itr);
    while (iter_next (itr, &val))
    {
		if((intptr_t)val < dist)
			dist = (intptr_t)val;
    }
    return (void *)dist;
}

int main(int argc, char *argv[]) 
{
    final_data_t final_vals;
    int i,j;
	FILE *fr;
	
	// Setup splitter args
    CHECK_ERROR((fr = fopen("graph.txt","r")) == NULL);
	CHECK_ERROR(fscanf(fr,"%d",&N) == 0);
	INFINITY = DIST*N;	
	int *adj = malloc(sizeof(int)*(N+1)*N);
	int shortest[N];
	int unit_size = sizeof(int)*(N+1);
	for(i = 0;i < N;i++)
	{
		for(j = 0;j < N;j++)
			CHECK_ERROR(fscanf(fr,"%d",&adj[i*(N+1)+1+j]) == 0);
	}
	for(i = 0;i < N;i++)
		shortest[i] = adj[1+i];
	
    CHECK_ERROR (map_reduce_init())
    // Setup map reduce args
    map_reduce_args_t map_reduce_args;
    memset(&map_reduce_args, 0, sizeof(map_reduce_args_t));
    map_reduce_args.task_data = adj;
    
    map_reduce_args.map = my_map;
    map_reduce_args.reduce = my_reduce;
    map_reduce_args.combiner = NULL;
    map_reduce_args.splitter = NULL;
    map_reduce_args.locator = my_locator;
    map_reduce_args.key_cmp = my_key_cmp;
    
    map_reduce_args.unit_size = unit_size;
    map_reduce_args.partition = NULL;
    map_reduce_args.result = &final_vals;
    map_reduce_args.data_size = unit_size*N;
    
    map_reduce_args.L1_cache_size = atoi(GETENV("MR_L1CACHESIZE"));	//1024 * 1024 * 2;
    map_reduce_args.num_map_threads = atoi(GETENV("MR_NUMTHREADS"));	//4;
    map_reduce_args.num_reduce_threads = atoi(GETENV("MR_NUMTHREADS"));	//4;
    map_reduce_args.num_merge_threads = atoi(GETENV("MR_NUMTHREADS"));	//4;
    map_reduce_args.num_procs = atoi(GETENV("MR_NUMPROCS"));			//4;
    map_reduce_args.key_match_factor = (float)atof(GETENV("MR_KEYMATCHFACTOR"));	//2;
	
	int change;
	do
	{
		short key;		
		change = 0;
		for(i = 0;i < N;i++)
			adj[i*(N+1)] = shortest[i];
		CHECK_ERROR(map_reduce (&map_reduce_args) < 0);
		for (i = 0; i < N && i < final_vals.length; i++)		
		{
			keyval_t * curr = &((keyval_t *)final_vals.data)[i];
			key = *((short *)curr->key);
			if(shortest[key] != (intptr_t)curr->val)
				change = 1;
			shortest[key] = (intptr_t)curr->val;			
		}
    }while(change);
    
    for (i = 0;i < N; i++)
		printf("%d %d\n",i,shortest[i]);
    
    CHECK_ERROR (map_reduce_finalize ());
    free(adj);
    return 0;
}
